﻿#include "FITKNastranAdaptorCase.h"
#include "FITKNastranAdaptorMAT1.h"
#include "FITKNastranAdaptorPSHELL.h"
#include "FITKNastranAdaptorPart.h"
#include "FITKNastranBDFIOInterface.h"
#include "FITKNastranBDFReader.h"

#define VIEWWINDOWSIZE 20

namespace Nastran
{
// Constructor for FITKNastranAdaptorCase, setting up adaptors for material, section, and part
FITKNastranAdaptorCase::FITKNastranAdaptorCase()
{
    // Initialize an instance of FITKAbaMaterial for material properties
    Interface::FITKAbaMaterial material;

    // Create and store the MAT1 adaptor for BDF format with the material instance
    _mat1Adaptor = FITKIOADAPTORFACTORY->createT<Nastran::FITKNastranAdaptorMAT1>("BDF", &material);

    // Initialize an instance of FITKAbaSectionShellHomogeneous for section properties
    Interface::FITKAbaSectionShellHomogeneous section;

    // Create and store the PSHELL adaptor for BDF format with the section instance
    _pshellAdaptor = FITKIOADAPTORFACTORY->createT<Nastran::FITKNastranAdaptorPSHELL>("BDF", &section);

    // Allocate a new FITKAbaqusPart for mesh data
    AbaqusData::FITKAbaqusPart *mesh = new AbaqusData::FITKAbaqusPart();

    // Create and store the Part adaptor for BDF format with the mesh instance
    _partAdaptor = FITKIOADAPTORFACTORY->createT<Nastran::FITKNastranAdaptorPart>("BDF", mesh);
}

FITKNastranAdaptorCase::~FITKNastranAdaptorCase()
{
    delete _mat1Adaptor;
    delete _pshellAdaptor;
    delete _partAdaptor;
}

bool FITKNastranAdaptorCase::adaptR()
{
    if (nullptr == getHelper() || nullptr == getReader() || nullptr == _caseData)
    {
        return false;
    }

    // create one Part for all pid
    AbaqusData::FITKAbaqusPart *wait2appendMesh = new AbaqusData::FITKAbaqusPart();

    AbaqusData::FITKAbaqusPartManager *partManager = _caseData->getPartManager();
    if (nullptr == partManager)
    {
        getReader()->consoleMessage(2, "nullptr error for part manager");
        return false;
    }
    else
    {
        wait2appendMesh->setDataObjectName(_caseData->getPartManager()->checkName("Part-"));
        _caseData->getPartManager()->appendDataObj(wait2appendMesh);
    }

    // file view window, size = 20 line
    QQueue<QString> fileViewWindow{};
    // when file atEnd and window are empty, loop stop
    while ((!(getReader()->atEnd())) || (0 != fileViewWindow.size()))
    {
        getReader()->sendCurrentPercent();

        if ((!(getReader()->atEnd())) && (fileViewWindow.size() < VIEWWINDOWSIZE))
        {
            fileViewWindow.enqueue(getReader()->readLine());
            continue;
        }

        QMap<QString, std::function<bool()>> readers;
        readers["PART"] = [&]()->bool {
            return this->readPart(wait2appendMesh, fileViewWindow);
        };

        readers["MAT1"] = [&]()->bool {
            return this->readMAT1(fileViewWindow);
        };

        readers["PSHELL"] = [&]()->bool {
            return this->readPSHELL(fileViewWindow);
        };

        readers["MAT1*"] = [&]()->bool {
            return this->readMAT1(fileViewWindow);
        };

        readers["PSHELL*"] = [&]()->bool {
            return this->readPSHELL(fileViewWindow);
        };

        QString command = fileViewWindow.head().left(8).simplified();

        if (isPartLine(fileViewWindow.head()))
        {
            command = "PART";
        }

        if (readers.contains(command)) 
        {
            if (!readers[command]())
            {
                getReader()->consoleMessage(2, "error for interpret " + command);
            }
        }

        fileViewWindow.dequeue();
    }

    getHelper()->constructGroupWithSection(wait2appendMesh);

    return true;
}

void FITKNastranAdaptorCase::setCaseData(AbaqusData::FITKDataCase *caseData)
{
    _caseData = caseData;
}

// Check if the line corresponds to a Nastran part line
bool FITKNastranAdaptorCase::isPartLine(QString line)
{
    // If line starts with any of the specified part identifiers
    if (line.startsWith("GRID") || line.startsWith("CTRIA3") || line.startsWith("CQUAD4") || line.startsWith("CHEXA") ||
        line.startsWith("CTETRA"))
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool FITKNastranAdaptorCase::readMAT1(QQueue<QString> cache)
{
    auto adaptor = _mat1Adaptor;

    // Check if the MAT1 adaptor is available
    if (nullptr == adaptor)
    {
        return false;
    }

    // Set necessary components for the MAT1 adaptor
    adaptor->setHelper(getHelper());
    adaptor->setReader(getReader());
    adaptor->setCaseData(_caseData);
    adaptor->setFileContentCache(cache);

    // Attempt to adapt the MAT1 data and store the result
    bool result = adaptor->adaptR();

    // Return the result of the adaptation process
    return result;
}

bool FITKNastranAdaptorCase::readPSHELL(QQueue<QString> cache)
{
    auto adaptor = _pshellAdaptor;

    // Check if the PSHELL adaptor is available
    if (nullptr == adaptor)
    {
        return false;
    }

    // Set necessary components for the PSHELL adaptor
    adaptor->setHelper(getHelper());
    adaptor->setReader(getReader());
    adaptor->setCaseData(_caseData);
    adaptor->setFileContentCache(cache);

    // Attempt to adapt the PSHELL data and store the resultFF
    bool result = adaptor->adaptR();

    // Return the result of the adaptation process
    return result;
}

// Function to read a part from Nastran data into the Abaqus mesh
bool FITKNastranAdaptorCase::readPart(AbaqusData::FITKAbaqusPart *mesh, QQueue<QString> cache)
{
    // Retrieve the part adaptor
    auto adaptor = _partAdaptor;

    // Check if the adaptor is null
    if (nullptr == adaptor)
    {
        return false;
    }

    // Set the helper, reader, case data, mesh part, and file content cache for the adaptor
    adaptor->setHelper(getHelper());
    adaptor->setReader(getReader());
    adaptor->setCaseData(_caseData);
    adaptor->setMeshPart(mesh);
    adaptor->setFileContentCache(cache);

    // Adapt the data and store the result
    bool result = adaptor->adaptR();

    return result;
}

} // namespace Nastran
